/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/
/*
Authors: Ram Ovadia 201374022 and Lior Fass 300069705
Project: HW Assignment 3 
Description: This is the implementation of the loader thread module used in the project.  
*/
/*oOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoOoO*/

#include "loaderThread.h"

/*
* makes a Uppercase duplicate of str1 in str2. str2 must be preallocated with the size of str1. 
* 
* Input:
* -----
* str1 - pointer to source string.
* str2 - pointer to destination string.    
*
* Output:
* -----
* 0 - successful
* str2 is used as output
*/
int stringToUpper(const char *str1, char* str2)
{
	unsigned int i;
	for (i = 0; i < strlen(str1)+1; i++)
	{
		str2[i] = toupper(str1[i]);
	}
	return 0;
}

/*
* reads the next destination from the current line, if we reached EOL return 0
* 
* Input:
* -----
* cur_line - pointer to the current line from file
* line_pointer - count the place in the line, gets updated in the function
*     
*
* Output:
* -----
* cur_dest - pointer to the 3 letters string of the next destination
* return 0 - if we reached end of line
* return 1 - if we have a valid destination
*/
int readCurrentDest(char* cur_line,char *cur_dest,int *line_pointer)
{
	if (cur_line[*line_pointer] != '\n' && cur_line[*line_pointer] != '\0')
	{
		if (cur_line[*line_pointer] == ' ')
			*line_pointer = *line_pointer +1;
		strncpy(cur_dest,cur_line + *line_pointer,SLOT_LEN);
		cur_dest[SLOT_LEN] = '\0';
		stringToUpper(cur_dest,cur_dest);
		*line_pointer = *line_pointer + SLOT_LEN;
		return 1; //success - continue
	}
	else
		return 0;
}

/*
* function used for thread termination
* 
* Input:
* -----
* err_msg - pointer to error message string  
*
* Output:
* -----
* Prints the relevant error message
* return 1 to notice that the thread encountered an error
*/
int terminate_loader_thread(char *err_msg)
{
	printf("%s", err_msg);
	return 1;
}


/*
* Load the empty slot that was found
* 
* Input:
* -----
* common - shared arguments containing the slots array and mutexes
* index - index of free slot
* cur_element - string of destination to be loaded
*
* Output:
* -----
* slots array being updated safely thanks to the lock
* return 0 if succeed, otherwise 1
*/
int LoadSlot(tArgs *common, int index, tSlot *cur_element)
{
	DWORD wait_result;
	wait_result = WaitForSingleObject(common->slots_mutexes[index],INFINITE);
	if (wait_result != WAIT_OBJECT_0)
		return terminate_loader_thread("Loader encountered problem in locking a slot");
	else
	{
		//Load the slot, release slot, and move to next dest
		memcpy(common->slots+index*SLOT_LEN,cur_element, SLOT_LEN);
		ReleaseMutex(common->slots_mutexes[index]);
	}
	return 0;
}

int loader_thread(void *args)
{
	char cur_line[LINE_LEN];
	char cur_dest[SLOT_LEN+1];
	int line_pointer;
	int i = 0;
	DWORD wait_result;
	tArgs *common = (tArgs *)args;

	// waiting for synchronization signal. 
	wait_result = WaitForSingleObject( common->sync_sempahore , INFINITE);
	if ( (wait_result == WAIT_ABANDONED) || (wait_result == WAIT_FAILED) )
	{
		return terminate_loader_thread("waiting on sync sempahore failed in Loader thread\r\n");
	}

	//Line Loop:
	while (fgets(cur_line, LINE_LEN, common->in_f) != NULL)
	{
		line_pointer = 0;
		//read 3 letters dest from line
		while(readCurrentDest(cur_line,cur_dest,&line_pointer) != 0)
		{
			// FOR ALL SLOTS
			for (i=0; i<common->num_of_slots; i++)
			{
				// if slot occupied then move to next slot
				if (strcmp(common->slots+i*SLOT_LEN, EMPTY_SLOT) != 0)
				{
					//if we reached last slot and it's occupied sleep and restart 
					if (i == common->num_of_slots - 1)
					{
						Sleep(10);
						i = -1;
					}
					continue;
				}
				// else load the slot
				else
				{
					if (LoadSlot(common, i, cur_dest) == 0)
						break;
					else
						return 1;
				}
			}
		}
	}
	// Reached end of File - RETURN EXIT CODE 0
	ReleaseSemaphore(common->sync_sempahore, 1, NULL);
	return 0;
}

